package com.abou.mvc.controller;

import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContext;
import com.abou.manager.common.IProductManager;
import com.abou.manager.exception.EManagerErrorCode;
import com.abou.manager.exception.ManagerException;

/**
 * The abstract class defining common methods for controllers (like exceptions
 * handling).
 * 
 * @author khalid
 */
public abstract class ACommonController {
  
  /**
   * The logger
   */
  private static final Logger LOGGER =
      Logger.getLogger(ACommonController.class);
  /**
   * The error message
   */
  public static final String ERROR_MSG = "Unexpected error";
  /**
   * The image product folder
   */
  public static final String IMG_PRODUCT_FOLDER = "/abou/res/productImages/";
  /**
   * The constant defining the name of the product model attribute
   */
  public static final String IMAGE_ATT1 = "image1";
  /**
   * The constant defining the name of the product model attribute
   */
  public static final String IMAGE_ATT2 = "image2";
  /**
   * The constant defining the name of the product model attribute
   */
  public static final String IMAGE_ATT3 = "image3";
  /**
   * The constant defining the name of the product model attribute
   */
  public static final String IMAGE_ATT4 = "image4";
  
  @Autowired
  private IProductManager productManager;
  private final String viewName;
  private String exPrevViewName;
  private boolean useDateTimeBinder = false;
  
  /**
   * The number of available rows numbers allowed by page for the search results
   * table
   */
  public static final String[] TABLE_ROWS_NBS_PER_PAGE = { "5", "10", "20" };
  
  /**
   * The attribute name for the products
   */
  public static final String PRODUCTS_ATT = "products";
  
  /**
   * The constructor
   * 
   * @param viewName See {@link #getViewName()}.
   * @param exPrevViewName See {@link #getExPrevViewName()}.
   */
  public ACommonController(String viewName, String exPrevViewName) {

    super();
    this.viewName = viewName;
    this.exPrevViewName = exPrevViewName;
  }
  
  /**
   * The constructor
   * 
   * @param viewName See {@link #getViewName()}.
   * @param exPrevViewName See {@link #getExPrevViewName()}.
   * @param useDateTimeBinder See {@link #isUseDateTimeBinder()}.
   */
  public ACommonController(String viewName, String exPrevViewName,
      boolean useDateTimeBinder) {

    this(viewName, exPrevViewName);
    this.useDateTimeBinder = useDateTimeBinder;
  }
  
  /**
   * @return The name for this view
   */
  public String getViewName() {

    return viewName;
  }
  
  /**
   * @return The name of the view to go when an exception occurs. Null if this
   *         view is the same as the controller's one.
   */
  public String getExPrevViewName() {

    return exPrevViewName;
  }
  
  /**
   * Sets the name of the view to go when an exception occurs.
   * 
   * @param exPrevViewName The name of the view to go when an exception occurs
   */
  public void setExPrevViewName(String exPrevViewName) {

    this.exPrevViewName = exPrevViewName;
  }
  
  /**
   * Inits a the binder for the controller
   * 
   * @param binder The current binder
   * @param request The current request
   * @param locale The current locale
   */
  @InitBinder
  public void initBinder(WebDataBinder binder, HttpServletRequest request,
      Locale locale) {

    ControllerUtils.initBinder(binder, request, locale);
  }
  
  /**
   * Handles exceptions of the passed type, thrown in the controller
   * 
   * @param exception A manager layer exception
   * @return Information on the view to display
   */
  @ExceptionHandler(Exception.class)
  public ModelAndView handleException(Exception exception) {

    LOGGER.error(ERROR_MSG, exception);
    
    ModelMap model = new ModelMap();
    
    Class<? extends Exception> exClass = exception.getClass();
    
    if (ManagerException.class.equals(exClass)) {
      
      ManagerException managerException = (ManagerException) exception;
      
      model.put(ExceptionController.EX_CODE_ATT,
          managerException.getCode().name());
      model.put(ExceptionController.EX_PARAMS_ATT, managerException.getParams());
      
    } else {
      
      model.put(ExceptionController.EX_CODE_ATT, EManagerErrorCode.UNEXPECTED);
      model.put(ExceptionController.EX_PARAMS_ATT, exception.getMessage());
    }
    
    String prevViewName = exPrevViewName;
    
    if (StringUtils.isBlank(prevViewName)) {
      
      prevViewName = viewName;
    }
    
    model.put(ControllerUtils.BACK_ATT, prevViewName
        + ControllerUtils.VIEW_NAME_SUFFIX);
    
    // TODO faire un redirect vers la bonne url => admin ou customer exception
    
    return new ModelAndView(
        ControllerUtils.REDIRECT_INSTR + ExceptionController.VIEW_NAME
            + ControllerUtils.VIEW_NAME_SUFFIX, model);
  }
  
  /**
   * Sets the name of the view to go when an exception occurs according to the
   * passed value.
   * 
   * @param back The page to go back in case of exception.
   */
  public void changeExPrevViewName(String back) {

    setExPrevViewName(back);
  }
  
  /**
   * @return Whether to use a date time binder instead of a date binder
   */
  public boolean isUseDateTimeBinder() {

    return useDateTimeBinder;
  }
  
  /**
   * Sets the new value for the useDateTimeBinder field.
   * 
   * @param useDateTimeBinder The new value for the useDateTimeBinder field
   */
  public void setUseDateTimeBinder(boolean useDateTimeBinder) {

    this.useDateTimeBinder = useDateTimeBinder;
  }
  
  /**
   * @return The current http request
   */
  public HttpServletRequest getHttpRequest() {

    HttpServletRequest request =
        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
    
    return request;
  }
  
  /**
   * @return A request context bound to the current thread.
   */
  public RequestContext createRequestContext() {

    return new RequestContext(getHttpRequest());
  }
  
  /**
   * @return The product manager
   */
  public IProductManager getProductManager() {

    return productManager;
  }
  
}
